//
// Created by mio on 25-5-13.
//

#include "VideoEncoder.h"
#include "H264Encoder.h"

VideoEncoder::VideoEncoder() : config_{}
{
    h264_encoder_.reset(new H264Encoder());
}

VideoEncoder::~VideoEncoder()
{
    Close();
}

bool VideoEncoder::Open(qint32 width, qint32 height, qint32 framerate, qint32 bitrate, qint32 format)
{
    config_.video.width = width;
    config_.video.height = height;
    config_.video.framerate = framerate;
    config_.video.bitrate = bitrate * 1000;
    config_.video.format = (AVPixelFormat)format;
    config_.video.gop = framerate;
    return h264_encoder_->Open(config_);
}

void VideoEncoder::Close()
{
    h264_encoder_->Close();
}

// TODO: 了解编码信息的作用
qint32 VideoEncoder::Encode(quint8 *rgba_buffer, quint32 width, quint32 height, quint32 size,
    std::vector<quint8> &out_frame)
{
    out_frame.clear();
    int frame_size = 0;
    int max_out_size = config_.video.width * config_.video.height * 4; // 设大一点 因为这个编码数据不会大于这个原始数据RGBA
    std::shared_ptr<quint8> out_buffer(new quint8[max_out_size], std::default_delete<quint8[]>());
    AVPacketPtr pkt = h264_encoder_->Encode(rgba_buffer, width, height, size);
    if (!pkt) return -1;

    quint32 extra_size = 0;
    quint8* extra_data = nullptr;
    // 判断是否是关键帧 如果是关键帧需要在264前面添加编码信息
    if (IsKeyFrame(pkt)) {
        // 获取编码信息
        extra_data = h264_encoder_->GetAVCodecContext()->extradata;
        extra_size = h264_encoder_->GetAVCodecContext()->extradata_size;
        // 编码信息放到包头去解析
        memcpy(out_buffer.get(), extra_data, extra_size);
        frame_size += extra_size;
    }

    memcpy(out_buffer.get() + frame_size, pkt->data, pkt->size); //264[编码信息 + 264裸流]
    frame_size += pkt->size;

    // 需要将数据传出去out_frame
    if (frame_size > 0) {
        out_frame.resize(frame_size);
        out_frame.assign(out_buffer.get(), out_buffer.get() + frame_size);
        return frame_size;
    }
    return 0;
}

qint32 VideoEncoder::GetSequenceParams(quint8 *out_buffer, qint32 out_buffer_size)
{
    quint32 size = 0;
    if (!h264_encoder_->GetAVCodecContext()) { return -1; }
    AVCodecContext* codecCtx = h264_encoder_->GetAVCodecContext();
    size = codecCtx->extradata_size;
    memcpy(out_buffer, codecCtx->extradata, size);
    return size;
}

bool VideoEncoder::IsKeyFrame(AVPacketPtr pkt)
{
    return pkt->flags & AV_PKT_FLAG_KEY;
}
